#ifndef RTC_CODEC_H
#define RTC_CODEC_H
#include <vector>
#include <string>
#include <array>
#include <iostream>
#include <fstream>

#include "asynctask.hpp"
#include "h256codec.h"
#include "xvideo_view.h"

using std::vector;
using std::string;
using std::array;
using std::cout, std::endl;

template <typename T>
using Vec = vector<T>;

using std::unique_ptr;
enum class FrameType {
    FrameTypeI = 0,
    FrameTypeP = 1,
};
struct RtpHeader {
    uint8_t version;
    uint8_t padding;
    uint8_t extension;
    uint8_t csrcCount;
    uint8_t marker;
    uint8_t payloadType;
    uint16_t sequenceNumber;
    uint32_t timestamp;
    uint32_t ssrc;
};
/// @brief  解析RTP包头
/// @param data 
/// @return 
RtpHeader parseRtpHeader(const std::vector<uint8_t> data) {
    RtpHeader header;
    header.version = uint8_t(data[0] >> 6) & 0x03;
    header.padding = uint8_t(data[0] >> 5) & 0x01;
    header.extension = uint8_t(data[0] >> 4) & 0x01;
    header.csrcCount = uint8_t(data[0]) & 0x0F;
    header.marker = uint8_t(data[1] >> 7) & 0x01;
    header.payloadType = uint8_t(data[1]) & 0x7F;
    // header.sequenceNumber = (data[2] << 8) | data[3];
    // header.timestamp = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
    // header.ssrc = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11];
    return header;
}


class RtcCodec {
private:
    AsyncTask* asyncTask_;
    using Bytes = vector<uint8_t>;
    // array<Vec<uint8_t>, 2> fragments_;
    // 第一个数据一定是I帧
    FrameType frameType_ = FrameType::FrameTypeI;
    // 有时候rtc中的数据会分成多个包，需要合并
    const uint8_t naluSplit[4] = {0x00, 0x00, 0x00, 0x01};
    bool isHaveDONL_ = false;
    // 当一帧数据过大时，需要分片发送，就需要缓存数据 其头部也要缓存
    bool isStartFrame_ = false;
    bool isEndFrame_ = false;
    uint8_t fuHeader_ = 0;
    uint16_t payloadHeader_ = 0;
    vector<uint8_t> cacheData_; 

    const static size_t H265BuffSize = 512 * 1024; // 512kb
    using H265BuffPtr = unique_ptr<Bytes>;
    using H265Buff = Bytes;

    // 写文件
    std::ofstream file_;

    H265Codec* h265Codec_ = nullptr;
    XVideoView* videoView_ = nullptr;

    bool isFirst = false;
public:
    using uchar = unsigned char;
    RtcCodec(bool ishaveDONL = false): isHaveDONL_(ishaveDONL) {
        file_.open("input.h265", std::ios::binary);
        if (!file_.is_open()) {
            cout << "open file failed" << endl;
        }
        const int rgbW = 800, rgbH = 600;
        videoView_ = XVideoView::Create();
        h265Codec_ = new H265Codec(rgbW, rgbH, AV_PIX_FMT_RGBA);
        // auto rgbaData = new uchar[rgbW * rgbH * 4];
        AVFrame *frame = av_frame_alloc();
        frame->format = AV_PIX_FMT_RGBA;
        frame->height = rgbH;
        frame->width = rgbW;
        frame->color_range = AVCOL_RANGE_JPEG; // 常见的 RGBA 范围设置
        int ret = av_frame_get_buffer(frame, 0);
        if (ret != 0) {
            cout << "av_frame_get_buffer failed" << endl;
        }
        asyncTask_ = new AsyncTask([=](const vector<uint8_t>& data) {
            if (data.size() == 0) {
                return;
            }
            // file_.write((const char*)data.data(), data.size());
            int linesize = rgbW * 4;
            if (!isFirst) {
                isFirst = true;
                cout << "init" << endl;
                videoView_->Init(rgbW, rgbH);
            }
            h265Codec_->decode(data.data(), data.size(), frame->data[0], frame->linesize[0]);
            videoView_->Draw(frame->data[0], frame->linesize[0]);
            // file_ << std::flush;
            // file_.write((char*)rgbaData, 800 * 600 * 4);
            // file_.write((const char*)rgbaData, 800 * 600 * 4);
            // file_ << std::flush
        }, H265BuffSize, 1);
    }
    ~RtcCodec() {
        asyncTask_->stop();
        delete asyncTask_;
        delete h265Codec_;
        delete videoView_;
        file_.close();
    }
    uint32_t getPayload(const std::vector<uint8_t>& data) {
        RtpHeader header = parseRtpHeader(data);
        size_t headerLength = 12 + header.csrcCount * 4;
        return headerLength;
    }
    void decodeH265(std::vector<uint8_t>& rtcData) {
        // 先假定不携带donl字段 返回RTP有效负载的起始长度
        auto h265DataStart = getPayload(rtcData);
        uint8_t* h265Data = rtcData.data() + h265DataStart;
        auto h265DataSize = rtcData.size() - h265DataStart;
        // 头部占两个字节，如果头部不完整，直接返回
        if (h265DataSize < 2) {
            return;
        }
        uint32_t startIdx = 0, endIdx = 0;
        // 取出第一个字节的中间六位，得到nalu的类型
        static bool is40 = false;
        // 在未得到第一个40
        if (h265Data[0] == 0x40) {
            is40 = true;
        }
        if (!is40) return;
        
        uint8_t naluType = ( h265Data[0] & 0x7E ) >> 1;
        // cout << "naluType: " << int(naluType) << endl;
        switch (naluType)
        {
        // 聚合包， 一个帧被分成多个包
        case 49: {
            // 前两个字节是nalu的header，后面一个是FusHeader
            if (h265DataSize < 3) {
                return;
            }
            startIdx = 3;
            endIdx = h265DataSize - 1;
            uint8_t fusHeader = h265Data[2];
            // fuheader 的前两位是分别表示否是起始帧和结束帧 起始和结束不可能同时出现
            bool isStart = fusHeader & 0x80;
            // bool isEnd = fusHeader & 0x40;
            if (isHaveDONL_) {
                cout << "这个包含donl字段" << endl;
            }
            // 如果是起始帧，需要缓存fuheader 和 payloadheader
            if (isStart) {
                this->isStartFrame_ = true;
                this->isEndFrame_ = false;
                // 第一个字节的中间六位和但三个字节的后六位是有关联的
                uint8_t naluType = h265Data[2] & 0x3F;
                uint8_t newHeader[2] {0};
                newHeader[0] = (h265Data[0] & 0x81) | naluType << 1;
                newHeader[1] = h265Data[1];
                // this->payloadHeader_ = (h265Data[0] << 8) | h265Data[1];
                // cout << std::hex << "newHeader: " << int(newHeader[0]) << " " << int(newHeader[1]) << endl;
                // exit(0);
                asyncTask_->append(naluSplit, 4);
                asyncTask_->append(newHeader, 2);
            } else {
                this->isStartFrame_ = false;
                this->isEndFrame_ = true;
            }
            asyncTask_->append(h265Data + startIdx, h265DataSize - 3);
            return;
        }
        // 多个帧在一个包里面 (为了减小复杂度，这里一个包只有一个帧 @@@ 抓包工具里面没看到有 60 开头的包) 也是认为不含有donl字段
        case 48: {
            // 先解析第一个nalu包 这个必有，后面还有可能有其他的nalu
            if (h265DataSize < 3) {
                return;
            }
            startIdx = 0;
            // 2 是header  后两个是 naluSize
            auto naluSize = (h265Data[1] << 8) | h265Data[2];
            endIdx = startIdx + naluSize;
            // 如果数据不完整，直接返回 应该不可能出现这种情况
            if (endIdx > h265DataSize) {
                return;
            }
            // 这里的数据是完整的一个包
            break;
        }
        // 其他情况都是完整的一个包
        default:
            // 还要把分隔符的数据加上
            startIdx = 0;
            endIdx = h265DataSize - 1;
            break;
        }
        this->asyncTask_->append(this->naluSplit, 4);
        this->asyncTask_->append(h265Data + startIdx, endIdx - startIdx + 1);
    }
};  
#endif